﻿using BCGPSCommon;
using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace BCGPSMongoData
{
    public class MongoDBCommand<T>
    {
        private IMongoClient client;
        private IMongoDatabase database;
        private IMongoCollection<T> collection;
        private static readonly IConfigurationRoot configuration;

        static MongoDBCommand()
        {
            configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .Build();
            var a = Directory.GetCurrentDirectory();
        }
        public MongoDBCommand(string collectionName)
        { 
            this.client = new MongoClient(  configuration["MongoDB:ConnectionString"]);
            this.database = client.GetDatabase( configuration["MongoDB:Database"]);
            this.collection = database.GetCollection<T>(collectionName);
        }
        public Task<IAsyncCursor<TResult>> Aggregate<TResult>(PipelineDefinition<T, TResult> pipelineDefinition)
        {
            return collection.AggregateAsync(pipelineDefinition);
        }

        public Task InsertDocumentAsync(T document)
        {
            return collection.InsertOneAsync(document);
 
        }

        public Task InsertDocumentAsync(IEnumerable<T> documents)
        {
            return collection.InsertManyAsync(documents);
        }

        public Task<DeleteResult> DeleteDocumentAsync(Expression<Func<T, bool>> func)
        {
            return collection.DeleteOneAsync(func);
        }

        public Task<DeleteResult> DeleteDocumentAsync(FilterDefinition<T> filter)
        {
            return collection.DeleteOneAsync(filter);
        }
        public Task<DeleteResult> DeleteDocumentsAsync(Expression<Func<T, bool>> func)
        {
            return collection.DeleteManyAsync(func);
        }

        public Task<DeleteResult> DeleteDocumentsAsync(FilterDefinition<T> filter)
        {
            return collection.DeleteManyAsync(filter);
        }
        public Task<T> UpdateDocumentAsync(Expression<Func<T, bool>> func, UpdateDefinition<T> update)
        {
            FindOneAndUpdateOptions<T> options = new FindOneAndUpdateOptions<T>
            {
                IsUpsert = false,
                ReturnDocument = ReturnDocument.After
            };

            return collection.FindOneAndUpdateAsync(func, update, options);
        }

        public Task<UpdateResult> UpdateDocumentsAsync(Expression<Func<T, bool>> func, UpdateDefinition<T> update)
        {
            return collection.UpdateManyAsync(func, update);
        }

        public Task<UpdateResult> UpdateDocumentsAsync(FilterDefinition<T> func, UpdateDefinition<T> update)
        {
            return collection.UpdateManyAsync(func, update);
        }

        public Task<T> ReplaceDocumentAsync(Expression<Func<T, bool>> func, T document)
        {
            return collection.FindOneAndReplaceAsync(func, document);
        }


        public T ReplaceDocument(Expression<Func<T, bool>> func, T document)
        {
            return collection.FindOneAndReplace(func, document);
        }
        /// <summary>
        /// 地图点在区域内
        /// </summary>
        /// <param name="IMEI"></param>
        /// <param name="lat">维度</param>
        /// <param name="lng">经度</param>
        /// <returns></returns>
        public Task<T> SelectDocumentAsync(string IMEI, double lng, double lat)
        {
            return collection.Find(" {IMEI:\""+IMEI+"\", Polygon: { $geoIntersects: { $geometry:{ \"type\" : \"Point\", \"coordinates\" : ["+lng+","+lat+"] } } } }").FirstOrDefaultAsync();
        }

        public Task<T> SelectDocumentAsync(Expression<Func<T, bool>> func)
        {
            return collection.Find(func).FirstOrDefaultAsync();
        }
        public Task<T> SelectDocumentAsync(Expression<Func<T, bool>> func, Expression<Func<T, object>> sortField, bool descending)
        {
            if (descending)
            {
                return collection.Find(func).SortByDescending(sortField).FirstOrDefaultAsync();
            }
            else
            {
                return collection.Find(func).SortBy(sortField).FirstOrDefaultAsync();
            }

        }
         
        public Task<T> SelectDocumentAsync(FilterDefinition<T> filter)
        {
            return collection.Find(filter).FirstOrDefaultAsync();
        }
         
        public Task<List<T>> SelectDocumentsAsync(Expression<Func<T, bool>> func)
        {
            return collection.Find(func).ToListAsync();
        } 
        public Task<List<T>> SelectDocumentsAsync(FilterDefinition<T> filter)
        {
            return collection.Find(filter).ToListAsync();
        }
        public Task<List<TResult>> SelectDocumentsAsync<TResult>(FilterDefinition<T> filter, ProjectionDefinition<T, TResult> fields)
        {
            return collection.Find(filter).Project(fields).ToListAsync();
        }
        public Task<List<TResult>> SelectDocumentsAsync<TResult>(Expression<Func<T, bool>> func, ProjectionDefinition<T, TResult> fields)
        {
            return collection.Find(func).Project(fields).ToListAsync();
        }
        public async Task<PageableList<T>> SelectDocumentsAsync(Expression<Func<T, bool>> func, Expression<Func<T, object>> sortField, bool descending, int skip, int limit)
        {
            PageableList<T> pageableList = new PageableList<T>
            {
                Count = await collection.CountDocumentsAsync(func)
            };

            if (descending)
            {
                pageableList.Items = await collection.Find(func).SortByDescending(sortField).Skip(skip).Limit(limit).ToListAsync();
            }
            else
            {
                pageableList.Items = await collection.Find(func).SortBy(sortField).Skip(skip).Limit(limit).ToListAsync();
            }

            return pageableList;
        }

        public async Task<PageableList<T>> SelectDocumentsAsync(FilterDefinition<T> filter, Expression<Func<T, object>> sortField, bool descending, int skip, int limit)
        {
            PageableList<T> pageableList = new PageableList<T>
            {
                Count = await collection.CountDocumentsAsync(filter)
            };

            if (descending)
            {
                pageableList.Items = await collection.Find(filter).SortByDescending(sortField).Skip(skip).Limit(limit).ToListAsync();
            }
            else
            {
                pageableList.Items = await collection.Find(filter).SortBy(sortField).Skip(skip).Limit(limit).ToListAsync();
            }

            return pageableList;
        }
        public async Task<PageableList<TResult>> SelectDocumentsAsync<TResult>(Expression<Func<T, bool>> func, ProjectionDefinition<T, TResult> fields, Expression<Func<T, object>> sortField, bool descending, int skip, int limit)
        {
            PageableList<TResult> pageableList = new PageableList<TResult>
            {
                Count = await collection.CountDocumentsAsync(func)
            };

            if (descending)
            {
                pageableList.Items = await collection.Find(func).SortByDescending(sortField).Skip(skip).Limit(limit).Project(fields).ToListAsync();
            }
            else
            {
                pageableList.Items = await collection.Find(func).SortBy(sortField).Skip(skip).Limit(limit).Project(fields).ToListAsync();
            }

            return pageableList;
        }
        public Task<T> RunCommandAsync(Command<T> command)
        {
            return database.RunCommandAsync(command);
        }
    }
}
